
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: AbstractSchemaUpdate.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar.schema.change;

import java.util.HashSet;
import java.util.Map;

import org.dellroad.sidekar.schema.SchemaModificationException;
import org.dellroad.sidekar.schema.model.EntityModel;
import org.dellroad.sidekar.schema.model.FieldModel;
import org.dellroad.sidekar.schema.model.IndexModel;
import org.dellroad.sidekar.schema.model.SchemaModel;
import org.dellroad.sidekar.schema.model.SimpleFieldModel;

/**
 * {@link SchemaChange} that adds a new index to some entity in the schema.
 */
public class AddIndexChange extends IndexChange {

    private final IndexModel indexModel;

    /**
     * Constructor.
     *
     * @param entityName name of the entity to which the index should be added
     * @param indexModel description of the index to add
     * @throws IllegalArgumentException if {@code entityName} is not a legal entity name
     * @throws IllegalArgumentException if the index's name is not a legal index name
     * @throws IllegalArgumentException if {@code indexModel} is null
     */
    public AddIndexChange(String entityName, IndexModel indexModel) {
        super(entityName, AddIndexChange.getIndexName(indexModel));
        this.indexModel = indexModel;
    }

    public IndexModel getIndexModel() {
        return this.indexModel;
    }

    @Override
    public String getDescription() {
        return "add " + this.indexModel + "' to entity `" + this.getEntityName() + "'";
    }

    @Override
    public void checkValidFor(SchemaModel schemaModel) {
        super.checkValidFor(schemaModel);
        final EntityModel entityModel = this.checkEntityExists(schemaModel, this.getEntityName());

        // Verify at least one field is listed
        if (this.indexModel.getFieldNames().isEmpty())
            throw new SchemaModificationException("indexed field name list is empty");

        // Verify no index already exists under the given name
        if (entityModel.getIndexModels().get(this.indexModel.getName()) != null) {
            throw new SchemaModificationException("an index named `"
              + this.indexModel.getName() + "' on " + entityModel + " already exists");
        }

        // Check indexed fields exist, are simple, and are only listed once
        Map<String, FieldModel> indexedEntityFields = entityModel.getFieldModels();
        HashSet<String> indexedFieldNames = new HashSet<String>();
        for (String fieldName : this.indexModel.getFieldNames()) {
            FieldModel fieldModel = indexedEntityFields.get(fieldName);
            if (fieldModel == null)
                throw new SchemaModificationException("indexed field `" + fieldName + "' does not exist in " + entityModel);
            if (!(fieldModel instanceof SimpleFieldModel))
                throw new SchemaModificationException("indexed field `" + fieldName + "' is not a simple field");
            if (indexedFieldNames.contains(fieldName))
                throw new SchemaModificationException("duplicate indexed field `" + fieldName + "'");
            indexedFieldNames.add(fieldName);
        }
    }

    @Override
    public final void visit(SchemaChangeSwitch schemaChangeSwitch) {
        schemaChangeSwitch.caseAddIndexChange(this);
    }

    private static String getIndexName(IndexModel indexModel) {
        if (indexModel == null)
            throw new IllegalArgumentException("null indexModel");
        return indexModel.getName();
    }
}

